// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright © 2018-2019 Ariadne Devos
/* sHT -- test the lexer for Kleene closures of letter sets */

/** Testing fragmentation in the lexer

  The HTTP header name can theoretically be fragmented, but typically,
  it isn't. Test fragmentation specially. TODO: test too long lexemes,
  unless it's common in some situation in s2. */

#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sHT/index.h>
#include <sHT/lex.h>
#include <sHT/test.h>

#define def_str(m, a) char m[sizeof(a) - 1] = a
/* Include some signed bytes as well,
  because <sHT/lex.c> used to pass potentially signed bytes
  to @var{sHT_bit_test}, which takes unsigned int.

  This is done by repeating the test with the bytes of @var{msg}
  and @var{c_allow} transposed from 0 to 128.

  This test case first had sign issues. On my x86-64 Linux system,
  signed/unsigned char did't really matter.

  GCC's -Werror=pointer-sign would have found the issue,
  although it gave no explanation.  */
static unsigned def_str(msg, "\x01\x02\x07\x03\x00\x05\x04\x03\x03");
static unsigned char c_allow[64] = { 0b10111111 };
static const unsigned char empty[53] = {};

static enum {
	TRANSPOSED_NOT,
	TRANSPOSED,
	END,
} transposed = 0;

_Noreturn
static size_t
fail_cb()
{
	if (transposed == TRANSPOSED_NOT) {
		if (printf("%s\n", "FAIL: fragmented/low") < 0)
			exit(2);
		if (printf("%s\n", "SKIP: fragmented/high") < 0)
			exit(2);
		exit(1);
	}
	if (printf("%s\n", "FAIL: fragmented/high\n") < 0)
		exit(2);
	exit(1);
}

static struct {
	struct sHT_lex_buf to;
	unsigned char bytes[53];
} to = { .to = { .offset = 0 }, .bytes = { } };
static struct sHT_lex_type c = {
	.cb_value = { &fail_cb, &fail_cb },
	.cb_ignore = &fail_cb,
	.c_allow = c_allow,
	.max_known = 30,
	.c_stop = 255,
};

static void
more(size_t ret, size_t offset)
{
	if (sHT_neq(sHT_lex(&to.to, msg + offset - ret, ret, &c), ret))
		fail_cb();
	/* Test offset incrementing */
	if (sHT_neq(to.to.offset, offset))
		fail_cb();
	size_t i;
	/* Correctly copied */
	sHT_index_iterate(i, offset) {
		if (sHT_neq(to.bytes[i], msg[i]))
			fail_cb();
	}
	/* Out-of-bound writes */
	if (memcmp(to.bytes + offset, empty, sizeof(to.bytes) - c.max_known))
		fail_cb();
}

_Noreturn
static size_t
expect_cb(struct sHT_lex_buf *arg_to, size_t n, size_t ret)
{
	if (arg_to != &to.to)
		fail_cb();
	/* ... excluding the terminator */
	if (sHT_neq(n, 9 + 1))
		fail_cb();
	if (sHT_neq(ret, 2))
		fail_cb();
	/* Correctly copied */
	size_t i;
	sHT_index_iterate(i, 9u) {
		if (sHT_neq(to.bytes[i], msg[i]))
			fail_cb();
	}
	if (sHT_neq(to.bytes[9], transposed ? 131 : 4))
		fail_cb();
	/* Out-of-bound writes */
	if (memcmp(to.bytes + sizeof(to.bytes) - c.max_known, empty, sizeof(to.bytes) - c.max_known))
		fail_cb();
	if (transposed == TRANSPOSED_NOT) {
		if (printf("%s\n", "PASS: fragmented/low") < 2)
			exit(2);
		transposed = TRANSPOSED;
	} else if (transposed == END) {
		if (printf("%s\n", "PASS: fragmented/high") < 2)
			exit(2);
		exit(0);
	}

	/* Retry with all bytes > 127*/
	transposed = 1;
	sHT_index_iterate(i, sizeof(msg)) {
		msg[i] += 128;
	}
	c_allow[16] = c_allow[0];
	c_allow[0] = 0;
	c.cb_value[1] = &fail_cb;
	c.c_stop = 0x10;
	memset(&to, 0, sizeof(to));

	more(2u, 2u);
	more(3u, 5u);
	more(1u, 6u);
	more(3u, 9u);
	transposed = END;
	c.cb_value[1] = &expect_cb;
	/* cb_value[1] must be called -- well-formed */
	sHT_lex(&to.to, (unsigned char *) "\x83\x10", 2, &c);
	fail_cb();
}


int
main(void)
{
	more(2u, 2u);
	more(3u, 5u);
	more(1u, 6u);
	more(3u, 9u);
	c.cb_value[1] = &expect_cb;
	/* cb_value[1] must be called -- well-formed */
	sHT_lex(&to.to, (unsigned char *) "\x04\xff", 2, &c);
	fail_cb();
}

